
package com.changgou.search.service.impl;

import com.alibaba.fastjson.JSON;

import com.changgou.goods.feign.SkuFeign;
import com.changgou.goods.pojo.Sku;
import com.changgou.search.dao.SkuInfoMapper;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SkuInfoService;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @Description TODO
 * @ClassName SkuInfoServiceImpl
 * @Author YPK
 * @Date 19:58 2019/11/15
 * @Version 2.1
 **/
@Service
public class SkuInfoServiceImpl  implements SkuInfoService {


    @Autowired
    private SkuInfoMapper skuInfoMapper;

    @Autowired
    private SkuFeign skuFeign;
    //    可以实现索引库的增删改查[高级搜索]
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;


    /**
     * @param searchMap
     * @param searchMap
     * @Author: YPK
     * @Date: 2019/11/15  21:28
     * @Description TODO      关键字搜索
     * @return: java.util.Map<java.lang.String
                       *       <   p>
     * //    @Override
     * //    public Map<String, Object> search(Map<String, String> searchMap) {
     * ////        // 构建检索条件(后期有多个条件检索，因此我们专门封装一个方法)
     * ////        NativeSearchQueryBuilder builder = builderBasicQuery(searchMap);
     * ////        // 根据关键字检索
     * ////        Map<String, Object> resultMap = searchForPage(builder);
     * ////
     * ////        return resultMap;
     * //        return null;
     * //    }
     * <p>
     * /**
     * @Author: YPK
     * @Date: 2019/11/15  22:50
     * @Description TODO     封装检索条件
     * @return: org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder
     * @Version: 1.0
     **/
    private NativeSearchQueryBuilder builderBasicQuery(Map<String, String> searchMap) {
//
//        String sq1 = "select * from tb_ sku where name like '%华为%'";
//        //预编译SQL语句,并日执行SQL
//        Connection conn = null;
//        PreparedStatement pst = conn.prepareStatement(sq1);
//        ResultSet resultSet = pst.executeQuery();
//        List<SkuInfo> skuInfoList = new ArrayList<SkuInfo>();
//        while (resultSet.next()) {
//            SkuInfo skuInfo = new SkuInfo();
//            skuInfo.setName(resultSet.getString("name"));
//            skuInfo.setCategoryName(resultSet.getString("categoryName"));
//            skuInfoList.add(skuInfo);
//        }
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        if (searchMap != null) {
            // 获取关键字
            String keywords = searchMap.get("keywords");
            if (!StringUtils.isEmpty(keywords)) {
                searchQueryBuilder.withQuery(QueryBuilders.matchQuery("name", keywords));
            }
        }
        return searchQueryBuilder;
    }

    /**
     * @param builder
     * @Author: YPK
     * @Date: 2019/11/15  22:44
     * @Description TODO   封装检索条件
     * @return: java.util.Map<java.lang.String
                       *       java.lang.Object>
     * @Version: 1.0
     **/
    public Map<String, Object> search(Map<String, String> searchMap) {
        //搜索条件封装
        NativeSearchQueryBuilder nativeSearchQueryBuilder = bulidBasicQuery(searchMap);
        //集合搜索
        Map<String, Object> resultMap = searchList(nativeSearchQueryBuilder);
        //查询显示内容
        //当用户选择了分类，将分类作为搜索条件，则不需要对分类进行分组搜索，因为分组搜索的数据是用于显示分类搜索条件的
        Map<String, Object> groupMap = searchGroupList(nativeSearchQueryBuilder, searchMap);
        resultMap.putAll(groupMap);
        return resultMap;
    }

    /**
     * @Author: YPK
     * @Description TODO  将数据库数据导入索引库中
     * @return: void
     * @Date: 2019/11/15  19:57
     * @Version: 1.0
     **/
    @Override
    public void importSkuInfoToEs() {
        // 通过feign调用商品微服务接口
//        List<Sku> skuList = skuFeign.findSkusByStatus("1").getData();
        List<Sku> skuList = skuFeign.findSkus().getData();
        //这个skuList  就是feign远程调用 goods 服务的 SkuController 中  findSkusByStatus方法  返回 的正常状态的库存信息
        // 将数据保存到索引库
        if (skuList != null && skuList.size() > 0) {
            String jsonString = JSON.toJSONString(skuList);
            List<SkuInfo> skuInfos = JSON.parseArray(jsonString, SkuInfo.class);
            // json串：{"手机屏幕尺寸":"5寸","网络":"联通3G+移动4G","颜色":"红",""机身内存":"16G","存储":"64G","像素":"800万像素"}
            for (SkuInfo skuInfo : skuInfos) {
                String spec = skuInfo.getSpec();
                Map<String, Object> specMap = JSON.parseObject(spec, Map.class);
                skuInfo.setSpecMap(specMap);
            }
            skuInfoMapper.saveAll(skuInfos);
        }
    }

    //组合条件查询
    private NativeSearchQueryBuilder bulidBasicQuery(Map<String, String> searchMap) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (searchMap != null && searchMap.size() > 0) {
            // 获取关键字
            if (!StringUtils.isEmpty(searchMap.get("keyword"))) {
                boolQueryBuilder.must(QueryBuilders.matchQuery("name", searchMap.get("keyword")));
            }
            //整词搜索
            if (!StringUtils.isEmpty(searchMap.get("category"))) {
                boolQueryBuilder.must(QueryBuilders.matchQuery("categoryName", searchMap.get("category")));
            }
            if (!StringUtils.isEmpty(searchMap.get("brand"))) {
                boolQueryBuilder.must(QueryBuilders.termQuery("brandName", searchMap.get("brand")));
            }
            //规格过滤实现:spec_网络=联通3G&spec_颜色=红
            for (Map.Entry<String, String> entry : searchMap.entrySet()) {
                String key = entry.getKey();
                //如果key以spec.开始，则表示规格筛选查询
                if (key.startsWith("spec_")) {
                    //规格条件的值
                    String value = entry.getValue();
                    boolQueryBuilder.must(QueryBuilders.termQuery("specMap." + key.substring(5) + ".keyword", value));
                }
            }
            //price 1500-2000元
            String price = searchMap.get("price");
            if (!StringUtils.isEmpty(price)) {
                //去掉中文元和以上0- 500 500 - 1000 1000- 1500 1500- 2000 2000- 3000 3000
                price = price.replace("元", "").replace("以上", "");
                //rices[]根据-分割[0, 500] [500, 1000]...... [3000]
                String[] prices = price.split("-");
                //x- -定不为空，y有可能为null
                if (prices != null && prices.length > 0) {
                    //prices[0]!-null
                    boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gt(Integer.parseInt(prices[0])));
                    //price > prices[0]
                    //prices[1]!-nul1 price<-prices[1]
                    if (prices.length == 2) {
                        // price<=prices[1]
                        boolQueryBuilder.must(QueryBuilders.rangeQuery("price").lte(Integer.parseInt(prices[1])));
                    }
                }
            }
        }
        //排序实现
        String sortField = searchMap.get("sortField"); //指定排序的域 .
        String sortRule = searchMap.get("sortRule");//指定 排序的规则  des asc
        if (!StringUtils.isEmpty(sortField) && !StringUtils.isEmpty(sortRule)) {
            nativeSearchQueryBuilder.withSort(new FieldSortBuilder(sortField).order(SortOrder.valueOf(sortRule)));
        }


        //分页，用户如果不传分页参数，则默认第1页
        Integer pageNum = converterPage(searchMap);
        Integer size = 3;
        nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNum - 1, size));

        //将boo1QueryBuilder填充给nativeSearchQueryBuilder
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
        return nativeSearchQueryBuilder;
    }

    //接收前端传入的分页参数
    public Integer converterPage(Map<String, String> searchMap) {
        if (searchMap != null) {
            String pageNum = searchMap.get("pageNum");
            try {
                return Integer.parseInt(pageNum);
            } catch (NumberFormatException e) {

            }
        }
        return 1;
    }


    private Map<String, Object> searchList(NativeSearchQueryBuilder nativeSearchQueryBuilder) {
        //高亮配置
        HighlightBuilder.Field field = new HighlightBuilder.Field("name"); //指定需要高亮域
        //前綴 <em sty1e=" color:red;">
        field.preTags("<em style=\"color:red;\">");
        //后缀</em>
        field.postTags("</em>");
        //碎片长度关键词数据的长今天小红穿了一件花衣服，好
        field.fragmentSize(100);
        nativeSearchQueryBuilder.withHighlightFields(field);

//        AggregatedPage<SkuInfo> page = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class);
        // 结果集处理
        AggregatedPage<SkuInfo> page = elasticsearchTemplate.queryForPage(
                nativeSearchQueryBuilder.build(),//搜索条件封装
                SkuInfo.class,// 数据集合要转换的类型字节码
                new SearchResultMapper() {//执行搜索后，将数据结果集封装到该对象中
                    @Override
                    public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> Class, Pageable pageable) {
                        // 存储所有转换后的高亮数据对象
                        List<T> list = new ArrayList<>();
                        for (SearchHit hit : searchResponse.getHits()) {
                            //结果集转为对象
                            SkuInfo skuInfo = JSON.parseObject(hit.getSourceAsString(), SkuInfo.class);
                            //分析结果集数据，获取高亮数据->只有某个域的高亮数据
                            //"name" -> "[name], fragments[[<em style="color:red;">小米</em>（MI）<em style="color:red;">小米</em>MIX2 硅胶保护套 蓝色]]"
                            HighlightField highlightField = hit.getHighlightFields().get("name");
                            if (highlightField != null && highlightField.getFragments() != null) {
                                // 高亮数据读取出来
                                Text[] fragments = highlightField.getFragments();
                                StringBuffer buffer = new StringBuffer();
                                for (Text fragment : fragments) {
                                    buffer.append(fragment.toString());
                                }
                                //非高亮数据中指定的域替换成高亮数据
                                skuInfo.setName(buffer.toString());
                            }
                            list.add((T) skuInfo);
                        }
                        return new AggregatedPageImpl<T>(list, pageable, searchResponse.getHits().getTotalHits());
                    }
                });
        List<SkuInfo> rows = page.getContent();         // 商品列表
        long totalElements = page.getTotalElements();   // 总条数
        int totalPages = page.getTotalPages();          // 总页数
        Map<String, Object> map = new HashMap<>();
        map.put("rows", rows);
        map.put("totalElements", totalElements);
        map.put("totalPages", totalPages);
        return map;
    }
    //AggregationBuilders.terms定义分组条件
    public Map<String, Object> searchGroupList(NativeSearchQueryBuilder nativeSearchQueryBuilder, Map<String, String> searchMap) {
        // skuCategory是别名
        if (searchMap == null || StringUtils.isEmpty(searchMap.get("catogory"))) {
            nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuCategory").field("categoryName"));
        }
        if (searchMap == null || StringUtils.isEmpty(searchMap.get("brand"))) {
            nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuBrand").field("brandName"));
        }
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuSpec").field("spec.keyword").size(10000));
        //显将结果全部搜索出来，然后再用上面条件分组去重复，取出分类字段名
        AggregatedPage<SkuInfo> aggregatedPage = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class);


        //定义一个Map，存储所有分组结果
        Map<String, Object> groupMapResult = new HashMap<String, Object>();
        //显将结果全部搜索出来，然后再用上面条件分组去重复，取出分类字段名
        if (searchMap == null || StringUtils.isEmpty(searchMap.get("catogory"))) {
            StringTerms categoryTerms = aggregatedPage.getAggregations().get("skuCategory");
            //获取分类分组集合数据
            List<String> categoryList = getGroupList(categoryTerms);
            groupMapResult.put("categoryList", categoryList);
        }
        //order by
        //显将结果全部搜索出来，然后再用上面条件分组去重复，取出分类字段名
        if (searchMap == null || StringUtils.isEmpty(searchMap.get("skuBrand"))) {
            StringTerms brandTerms = aggregatedPage.getAggregations().get("skuBrand");
            //获取品牌分组集合数据
            List<String> brandList = getGroupList(brandTerms);
            groupMapResult.put("brandList", brandList);
        }
        //specTrems
        StringTerms specTrems = aggregatedPage.getAggregations().get("skuSpec");
        //获取规格分组集合数据
        List<String> specList = getGroupList(specTrems);

        Map<String, Set<String>> specMap = putAllSpec(specList);
        groupMapResult.put("specList", specMap);
        return groupMapResult;
    }

    private List<String> getGroupList(StringTerms stringTerms) {
        List<String> groupList = new ArrayList<String>();
        for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
            String categoryName = bucket.getKeyAsString();//其中的一个分类名字
            groupList.add(categoryName);
        }
        return groupList;
    }



    private Map<String, Set<String>> putAllSpec(List<String> specList) {
        Map<String, Set<String>> allSpec = new HashMap<String, Set<String>>();
        for (String spec : specList) {
            Map<String, String> specMap = JSON.parseObject(spec, Map.class);
            //4.合并流程，循环所有Map
            for (Map.Entry<String, String> entry : specMap.entrySet()) {
                //4.1取出当前Map，并且获取对应的Key以及对应value
                String key = entry.getKey();//规格名字
                String value = entry.getValue();//规格值
                //4. 2将当前循环的数据合并到- -个Map<String, Set<String>>中
                Set<String> specSet = allSpec.get(key);
                if (specSet == null) {
                    specSet = new HashSet<String>();
                }
                specSet.add(value);
                allSpec.put(key, specSet);
            }
        }
        return allSpec;
    }
}